package br.edu.ufabc.lnc.peanho.presenter;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import java.awt.image.BufferedImage;

import java.util.ArrayList;
import java.util.Properties;
import java.util.concurrent.ExecutionException;

import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

import br.edu.ufabc.lnc.peanho.fuzzylogic.InferenceEngine;
import br.edu.ufabc.lnc.peanho.util.ImageUtilities;

import br.edu.ufabc.lnc.peanho.view.Canvas;
import br.edu.ufabc.lnc.peanho.view.InputReadingsWindow;
import br.edu.ufabc.lnc.peanho.view.MenuBar;
import br.edu.ufabc.lnc.peanho.view.Sprite;
import br.edu.ufabc.lnc.peanho.view.MainWindow;

public class MainCanvasPresenter {

	private MainWindow mainWindow;
	private Canvas canvas;
	private BufferedImage background;
	private BufferedImage backBuffer;
	private ArrayList<Sprite> sprites;

	public MainCanvasPresenter(Properties configuration) {
		sprites = new ArrayList<Sprite>();
		background = ImageUtilities.loadImage(configuration
				.getProperty("background"));
		backBuffer = ImageUtilities.createCompatibleImage(
				background.getWidth(), background.getHeight(), background
						.getTransparency(), null);
		BufferedImage router = ImageUtilities.loadImage(configuration
				.getProperty("router"));
		BufferedImage interestPoint = ImageUtilities.loadImage(configuration
				.getProperty("interest-point"));
		BufferedImage userImage = ImageUtilities.loadImage(configuration
				.getProperty("user"));
		int[] readings1 = { 0, 0, 0 };
		int[] readings2 = { 0, 0, 0 };
		int[] readings3 = { 0, 0, 0 };
		int[] readings4 = { 60, 35, 75 };
		int[] readings5 = { 80, 60, 45 };
		int[] readings6 = { 45, 80, 65 };
		int[] userReadings = { 80, 25, 60 };
		sprites.add(new Sprite("User", userReadings, userImage, 495, 526));
		sprites.add(new Sprite("AP-1", readings1, router, 269, 531));
		sprites.add(new Sprite("AP-2", readings2, router, 290, 212));
		sprites.add(new Sprite("AP_3", readings3, router, 558, 353));
		sprites.add(new Sprite("Phone1", readings4, interestPoint, 728, 495));
		sprites.add(new Sprite("Phone2", readings5, interestPoint, 243, 387));
		sprites.add(new Sprite("Phone3", readings6, interestPoint, 125, 100));

	}

	public void drawBackBuffer() {
		Graphics2D g2d = backBuffer.createGraphics();
		g2d.drawImage(background, 0, 0, canvas);
		g2d.dispose();
		for (Sprite sprite : sprites) {
			sprite.drawSprite(backBuffer);
		}
	}

	public void showWindow() {
		Runnable runnable = new Runnable() {

			@Override
			public void run() {

				mainWindow.setVisible(true);
				canvas.repaint();
			}
		};
		SwingUtilities.invokeLater(runnable);
	}

	public void createWindow() {
		drawBackBuffer();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				MenuBar menuBar = new MenuBar(MainCanvasPresenter.this);
				canvas = new Canvas(backBuffer);
				SpriteListener spriteListener = new SpriteListener(
						MainCanvasPresenter.this);
				canvas.addMouseListener(spriteListener);
				canvas.addMouseMotionListener(spriteListener);
				mainWindow = new MainWindow();
				mainWindow.setLayout(new BorderLayout());
				mainWindow.setJMenuBar(menuBar);
				mainWindow.add(canvas, BorderLayout.CENTER);
				mainWindow.pack();
			}
		};
		SwingUtilities.invokeLater(runnable);
	}

	public void updateScreen() {
		if (SwingUtilities.isEventDispatchThread()) {
			drawBackBuffer();
			canvas.repaint();
		}

	}

	/**
	 * Retorna a sprite selecionada pelo com o mouse, ou null em caso de nenhuma
	 * ter sido selecionada.
	 * 
	 * @param x
	 *            posição x do mouse
	 * @param y
	 *            posição y do mouse
	 * @return {@link Sprite} selecionada ou null
	 */
	public Sprite getSelectedSprite(int x, int y) {
		Sprite selectedSprite = null;
		for (Sprite sprite : sprites) {
			if (sprite.contains(x, y)) {
				selectedSprite = sprite;
				break;
			}
		}
		return selectedSprite;
	}

	public void requestReadings(final Point point) {

		Sprite sprite = getSelectedSprite(point.x, point.y);
		if (sprite == null) {

		} else {
			InputReadingsWindow.requestReadings(mainWindow, sprite.getReadings());
			//sprite.setReadings(sprite.getReadings());
			for (int i : sprite.getReadings())
				System.out.println(i);
		}
	}

	public void showComputationResult(String interestPointId) {
		boolean found = false;
		if (interestPointId != null) {
			for (Sprite sprite : sprites) {
				if (sprite.getId().equals(interestPointId)) {
					Graphics2D g2d = backBuffer.createGraphics();
					g2d.setColor(Color.RED);
					Point position = sprite.getPosition();
					g2d.drawOval(position.x-10, position.y-10, 60, 60);
					found = true;
					break;
				}
			}
			if (found) {
				canvas.repaint();
			}
		}
	}

	private static class SpriteListener extends MouseAdapter {

		private MainCanvasPresenter presenter;
		private Sprite selectedSprite;
		private boolean spriteSelected = false;

		public SpriteListener(MainCanvasPresenter presenter) {
			this.presenter = presenter;
		}

		@Override
		public void mousePressed(MouseEvent mouseEvent) {
			Sprite sprite = presenter.getSelectedSprite(mouseEvent.getX(),
					mouseEvent.getY());
			if (sprite != null) {
				selectedSprite = sprite;
				spriteSelected = true;
			}
		}

		@Override
		public void mouseDragged(MouseEvent mouseEvent) {
			if (spriteSelected) {
				selectedSprite.updatePosition(mouseEvent.getX(), mouseEvent
						.getY());
				presenter.updateScreen();
			}
		}

		@Override
		public void mouseReleased(MouseEvent mouseEvent) {
			if (spriteSelected) {
				selectedSprite.updatePosition(mouseEvent.getX(), mouseEvent
						.getY());
				presenter.updateScreen();
				spriteSelected = false;
			}
		}

		@Override
		public void mouseClicked(MouseEvent mouseEvent) {
			if (SwingUtilities.isRightMouseButton(mouseEvent)) {
				presenter.requestReadings(mouseEvent.getPoint());
			}
		}
	}

	/**
	 * Executa a simulação.
	 */
	public void startSimulation() {
		SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {

			@Override
			protected String doInBackground() throws Exception {
				InferenceEngine inferenceEngine = new InferenceEngine();
				inferenceEngine.configure(sprites.subList(4, sprites.size()));
				String interestPointId = inferenceEngine
						.computeNearestInterestPoint(sprites.get(0).getReadings());
				return interestPointId;
			}

			@Override
			protected void done() {
				try {
					String interestPointId = get();
					showComputationResult(interestPointId);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ExecutionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
		};
		worker.execute();
	}
}
